// 谈谈垃圾回收机制方式及内存管理
{
}

// Ajax的优缺点及工作原理？
{
  /* 
  Ajax 是一种用于创建快速动态网页的技术。Ajax 是一种在无需重新加载整个网页的情况下，能够更新部分网页的技术。
  传统的网页（不使用 Ajax）如果需要更新内容，必须重载整个网页页面。

  优点：
  1.减轻服务器的负担,按需取数据,最大程度的减少冗余请求
  2.局部刷新页面,减少用户心理和实际的等待时间,带来更好的用户体验
  3.基于xml标准化,并被广泛支持,不需安装插件等,进一步促进页面和数据的分离

  缺点：不利于SEO，
  1.AJAX大量的使用了javascript和ajax引擎,这些取决于浏览器的支持.在编写的时候考虑对浏览器的兼容性.
  2.AJAX只是局部刷新,所以页面的后退按钮是没有用的.
  3.对流媒体还有移动设备的支持不是太好等

  工作原理：
  1.创建ajax对象（XMLHttpRequest/ActiveXObject(Microsoft.XMLHttp)）
  2.判断数据传输方式(GET/POST)
  3.打开链接 open()
  4.发送 send()
  5.当ajax对象完成第四步（onreadystatechange）数据接收完成，判断http响应状态（status）200-300之间或者304（缓存）执行回调函数
  */
}

// 请指出document load和document ready的区别？
{
  /* 
  ready 事件的触发，表示文档结构已经加载完成（不包含图片等非文字媒体文件）。
  onload 事件的触发，表示页面包含图片等文件在内的所有元素都加载完成。
  */
}

// node和浏览器的event loop有什么区别？
{
  /* 
    浏览器和Node 环境下，microtask 任务队列的执行时机不同
    Node端，microtask 在事件循环的各个阶段之间执行
    浏览器端，microtask 在事件循环的 macrotask 执行完之后执行
  */
}

// promise和Rxjs的区别
{
  /* 
  1、rxjs可以取消subscribe，promise不可以
  2、rxjs可以发射多次，promise只能发射一次
  3、rxjs自带了许多的工具函数，如filter等
  4、rxjs通过和第三方插件组合使用，如zone可以让流横行按自己指定方式处理，默认rxjs是纵向顺序执行
  */
}

// cookie的存储格式
{
  document.cookie = 'test2=myCookie2; domain=.google.com.hk; path=/webhp';
  /* 
  cookie整体是一个字符串，每个 cookie 都以名/值对的形式，即 name=value，名称和值都必须是URL编码的，且两对cookie间以分号和空格隔开。
  */
}

// es7 8中增加了哪些规范
{
  /* 
  在es7中添加了以下二个规范
  1、添加了数组的includes方法，用来判断数组中是否有此值，在数组验证值中代码更清晰了，不需要indexOf判断是否-1

  2、添加指数操作符**，**具有与Math.pow(..)等效的计算结果 2**10 输出1024

  在es8中添加了几下几个规范
  1、async await 让异步处理不用嵌套多个层级，代码层面通过await修饰看起来像同步
  2、Object.values()、Object.entries() 返回对象的值及返回对象的key-value，一个是为了省去遍历对象的key，另一个者返回对象可枚举的属性值

  3、string中增加了字符串处理的规范，String.prototype.padStart和String.prototype.padEnd，允许将空字符串或其他字符串添加到原始字符串的开头或结尾。
  console.log('0.0'.padEnd(4,'0')) //0.00    
  console.log('0.0'.padEnd(10,'0'))//0.00000000

  4、getOwnPropertyDescriptors函数用来获取一个对象的所有自身属性的描述符,如果没有任何自身属性，则返回空对象。
  5、SharedArrayBuffer对象、Atomics对象，第一个是二进制数据缓冲区，另一个是针对此对象的原子锁，这些原子操作属于 Atomics 模块。与一般的全局对象不同，Atomics 不是构造函数，因此不能使用 new 操作符调用，也不能将其当作函数直接调用。Atomics 的所有属性和方法都是静态的（与 Math  对象一样）。
多个共享内存的线程能够同时读写同一位置上的数据。原子操作会确保正在读或写的数据的值是符合预期的，即下一个原子操作一定会在上一个原子操作结束后才会开始，其操作过程不会中断。

  */
}

/* 
// 缓存题
<!-- 假设我们有一个HTML页面，如下:page.html -->
<!DOCTYPE html>
http://www.w3.org/1999/xhtml">
<head>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
    <title>page页</title>
</head>
<body>
    <img src="images/head.png" />
    <a href="page.html">重新访问page页</a>
</body>
</html>
*/
/* 
  加载此页面后，会获取图片，图片请求返回的响应头为
  HTTP/1.1 200 OK
  Cache-Control: no-cache
  Content-Type: image/png
  Last-Modified: Tue, 08 Nov 2016 06:59:00 GMT
  Accept-Ranges: bytes
  Date: Thu, 10 Nov 2016 02:48:50 GMT
  Content-Length: 3534
*/
{
  // 问题一：当点击“重新访问 page 页”链接重新加载该页面后， head.png 如何二次加载？
  // 问题二：如果将上述信息中的 Cache-Control 设置为 private，那么结果又会如何呢？
  /* 答：
  1、因为Cache-Control: no-cache缓存前都会先判断资源是否缓存过期，所以图片会发出请求头带上If-Modified-Since: Tue, 08 Nov 2016 06:59:00 GMT，服务器确认新鲜度，如果客户端资源是新鲜资源则返回304,否则返回200并带上新的图片资源
  
  2、当Cache-Control: private之后，响应头没有给到任何缓存策略，浏览器会自己添加一个默认缓存机制，即 Expires = 当前时间(Date - Last-Modified) * 10%,简单理解就是响应头的Date时间与Last-Modified的时间差的十分之一作为缓存的过期时间。
  按照这个处理流程，如果马上重新加载，则会直接读取本地缓存内容 ，无需向服务器请求。
  */
}

// ---------------以下是js知识----------------- //
// JS中typeof的类型有哪些;
{
  console.log(typeof undefined); //undefined
  console.log(typeof 123); //number
  console.log(typeof '123'); //string
  console.log(typeof true); //boolean
  console.log(typeof [1, 2, 3]); //object
  console.log(typeof { id: 11 }); //object
  console.log(typeof null); //object
  console.log(typeof console.log); //function
}

// JS中有哪些内置函数
{
  /* 内置函数是可以直接运行的，有如下：
    Object
    Array
    Boolean
    Number
    String
    Function
    Date
    RegExp
    Error
  */
}

// JS变量按照存储方式有哪些类型
{
  /*
  1.值类型
  2.引用类型（节省空间，公用内存块）

  JS中数据对于存储可以分为两种数据类型：基本类型和引用类型
  基本类型：Number，String，Boolean，Null，Undefined
  这些类型的值存放在栈区，函数调用时传递的是变量的值(值)。
  引用类型：Object，Array，Function
  这些类型的对象存放在堆区，对象的地址存放在栈区，函数调用时传递的是对象的地址(址)。
  */
}

// 如何理解JSON
{
  /* 
  JSON是JS的一个对象，也是一种数据格式，JSON中的两个api如下
  将JSON字符串转换成JSON对象 JSON.parse()
  将JSON对象转换成JSON字符串 JSON.stringify()
  */
}

// typeof undefined == typeof NULL的值是什么？
{
  console.log(typeof undefined == typeof NULL); // 输出什么？

  // 该表达式将被评估为true，因为NULL将被视为任何其他未定义的变量。
}

// 使用typeof bar ===“object”来确定bar是否是一个对象时有什么潜在的缺陷？这个陷阱如何避免
{
  /* 
  尽管typeof bar ===“object”是检查bar是否是对象的可靠方法，但JavaScript中令人惊讶的问题是null也被认为是一个对象！

  因此，对于大多数开发人员来说，下面的代码会将真实（而不是错误）记录到控制台：
  var bar = null;
  console.log(typeof bar === "object");  // logs true!
  */
}

// 判断一个变量是否是数组类型，不能使用es6以上方法
{
  var obj = [{ a: 22 }];

  toString.call(obj); // 通过借用toString方法，把数组或是对象打印出来，输出[object Array]
  console.log(obj.constructor === Array); // 通过原型链上找是否是数组 [Function: Array]
  console.log(obj instanceof Array); // 判断是否是Array实现
}

// 如何理解作用域及闭包
{
  /* 
  自由变量
  作用域链，及自由变量的查找，找不到逐级向上找
  闭包的两个场景：函数作为变量传递、函数作为返回值

  闭包能够读取其它函数内部数据（变量/函数等），只有函数内部子函数才能读取到的局部变量。
  
  */
}

// 同步和异步的区别，分别列举一个同步和异步的例子
{
  /* 
  同步会阻塞代码，但是异步不会 alert是同步 setTimeout是异步
  */
}

// 常用的数组api知道哪些？说一下，并且解释一下每个api的含义
{
  /* 这是一个开放性题目，看你了解多少，以下只是举例部份，你可以自己尝试说出更多
  forEach（遍历所有元素）
  map（对数组进行重新组装，生成新的数组）
  sort（对数组进行排序）
  filter（过滤符合条件的元素）
  every（判断所有元素是否都符合要求）
  some（判断是否有至少一个元素符合条件）
  join（根据条件对数组组合成字符串）
  reverse（将数组反转）
  */
}

// 常用的对象api
{
  /* 这是一个开放性题目，看你了解多少，以下只是举例部份，你可以自己尝试说出更多
  obj.hasOwnProperty（检查属性是不是对象自有的，排除从原型链找到的属性）
  obj.values() 返回对象上所有value值，以数组形式
  obj.keys() 返回对象上所有key值，以数组形式
  Object.entries()方法返回一个给定对象自身可枚举属性的键值对数组
  Object.assign()这个方法来实现浅复制
   */
}

// 实现getTags方法，获取html页面中所有标签信息
{
  /* html类似结构这样
  <body>
    <div></div>
    <p></p>
  </body>
  */
  function getTags() {}
  getTags(); // 返回 ['html', 'head', 'title', 'meta', 'body', 'div', 'p'];

  // 实现如下
  function getTags() {
    var dom = document.getElementsByTagName('*');
    var resDom = [];
    var hashKey = {};
    var tempDomName;
    for (var i = 0; i < dom.length; i++) {
      tempDomName = dom[i].tagName.toLocaleLowerCase();
      if (!hashKey[tempDomName]) {
        resDom.push(tempDomName);
        hashKey[tempDomName] = tempDomName;
      }
    }
    delete tempDomName;
    delete hashKey;
    return resDom;
  }
}

// 下面打印的结果是什么？为什么？
{
  if (!(b in window)) {
    var b = 2;
    a += 1;
  } else {
    a += 2;
  }
  console.log(a);
  console.log(b);

  // 在判断b是否在window中就会报错，所以本身就会走到else层，而运算符优先级关系，所以导致a变量也不能提升，所以在+=的时候就会报错输出a is not defind
}

// 下面打印的结果是什么？为什么？
{
  var m = 1;
  function log() {
    var m = 2;
    return function () {
      m += 1;
    };
  }
  var _log = log();
  _log();
  console.log(m);
  // 因为log函数内返回的是一个函数，而返回的函数属于闭包，会产生属性自己的av,go，
  // 此时这个匿名函数的go:{m=2} ao:{fn}，当执行这个闭包的时候，读取的是自己作用域内的go，所以3
}

// 下面打印的结果是什么？为什么？
{
  for (var i = 0; i < 5; i++) {
    (function () {
      setTimeout(function () {
        console.log(i);
      }, 1000);
    })(i);
  }
  // 这个循环内用了一个闭包，把每个i传入了，但是闭包匿名函数接并没有接收这个变量，所以读取的依然还是外部的i变量，本身作用域内的ao并没有这个值，输出和正常没有闭包效果一样
}

// 下面打印的结果是什么？为什么？
{
  function fun() {}
  console.log(typeof fun);
  console.log(fun instanceof Function);
  console.log(fun instanceof Object);
  // 原型指向的是objec，万物皆对象，但是对象的实例指向的根源还是Function，所以都是true
}

// 以下打印的是什么？为什么？
{
  let person = { name: 'hello' };
  const members = person;

  person = null;
  console.log(members);

  /* 
  还是输出person原有的值 { name: 'hello' }。 虽然members赋值的是按引用传递的，改变person的值也会跟着变，但是当赋值null时，会等下次GC回收才把原始指针的内容清空。
  此时按引用传递的一直是指向原始指针的，所以在被null时，members还是原来的值，GC还未被回收原始指针的内容成null
  */
}

// 下面打印的结果是什么？为什么？
{
  var a = 1;
  var obj = {
    a: 2,
    getA: function () {
      return this.a;
    },
  };
  console.log(obj.getA());
  console.log(obj.getA.call());
  console.log(obj.getA.call({ a: 10 }));
  /*
  第一个正常读取，读取自己作用域内的值2，优先读取自身，然后访问a.prototype.name
  第二个call改变了this指向，在没有传入参数的情况下，是指向window
  第三个直接指定了this指向，所以读取的就是传入参数对象里面的值10
  */
}

// 下面打印的结果是什么？为什么？
{
  var arr = [1, 2, 3];
  function test(arr) {
    arr = [];
  }
  test(arr);
  console.log(arr);
  // 因为传入的参数是按值传递的，里面改变这个值，并不会影响外部，打印依然是原来的值
}

// 构造函数Fn，原型对象，实例对象，三者之间的关联关系
{
  /**
   每创建一个函数，该函数都会自动带有一个prototype属性。该属性是一个指针，指向一个对象，该对象称之为原型对象(后期我们可以使用这个原型对象帮助我们在js中实现继承).

   原型对象上默认有一个属性constructor,该属性也是一个指针，指向其相关联的构造函数。

   通过调用构造函数产生的实例对象，都拥有一个内部属性，指向了原型对象。其实例对象能够访问原型对象上的所有属性和方法。

   总结：三者的关系是，每个构造函数都有一个原型对象，原型对象上包含着一个指向构造函数的指针，而实例都包含着一个指向原型对象的内部指针。通俗的说，实例可以通过内部指针访问到原型对象，原型对象可以通过constructor找到构造函数
  */
}

// var const let 的区别
{
  /*
  var会变量声明，作用域不是太固定，es5老语法因此很多考this读取变量
  const声明会有自身作用域内才可以读取到，并且不能被修改值
  let 声明会在自己的作用域块内，是可以改变值的
  */
}

// 移动端点击事件300ms延迟如何去掉？原因是什么
{
  // 可以把a链接阻止掉默认事件，绑定事件利用window.location.href = 当前a链接进行跳转
  // 或是使用一个FastClick库
  // 移动端事件触发顺序：在移动端，手指点击一个元素，会经过：touchstart-- > touchmove -> touchend-- > click。
}

// 下面打印的结果是什么？
{
  function Foo() {
    getName = function () {
      console.log(1);
    };
    return this;
  }
  Foo.getName = function () {
    console.log(2);
  };
  Foo.prototype.getName = function () {
    console.log(3);
  };
  var getName = function () {
    console.log(4);
  };
  function getName() {
    console.log(5);
  }

  Foo.getName(); //
  getName(); //
  Foo().getName(); //
  getName(); //
  new Foo.getName(); //
  new Foo().getName(); //
  new new Foo().getName(); //
}

// 请回答以下代码的运行结果是什么，并写出为什么？
{
  var employeeId = 'abc123';
  function foo() {
    employeeId();
    return;
    function employeeId() {
      console.log(typeof employeeId);
    }
  }
  foo();
  // 函数优先，function
}

// 请问person的值为？？ A：xyz  B：Alex  C：USA
// 并且简述这段代码为什么会得到你选择的结果？
{
  function User(name) {
    this.name = name || 'Alex';
  }
  var person = (new User('xyz')['location'] = 'USA');
  console.log(person);
}

// 请回答一下代码运行结果是什么，以及为什么
{
  var salary = '1000$';
  (function () {
    console.log('Original salary was' + salary);
    var salary = '5000$';
    console.log('My New Salary' + salary);
  })();

  // undefined 和 5000$，因为在闭包环境中，有相同变量，所有变量提升作用域，但还未赋值，所以第一个log会输出undefined，而第二个log是在此值赋值后才读取，所以会读取到5000$
}

// 请实现()()这样的一个方法，可以一直调用自身函数，函数式编程思想
{
  const a = sum(); // a === 0
  const b = sum(2)(); // b === 2
  const c = sum(4)(5)(); // c === 9
  // const k = sum(n1)....(nk)() // k === n1 + ... + nk
}

// for in 和 for of 区别？
{
  Array.prototype.method = function () {
    console.log(this.length);
  };
  var myArray = [1, 2, 4, 5, 6, 7];
  myArray.name = '数组';
  for (var index in myArray) {
    console.log(myArray[index]);
  } // 1 2 3 4 5 6 7 数组 fn() {console.log(this.length)}

  for (var value of myArray) {
    console.log(value);
  } // 1 2 3 4 5 6 7

  /* 
  for in循环
  1、可以遍历普通对象，并且会遍历可枚举的属性（含原型上的属性和方法），所以需要过滤掉原型属性

  for of循环可遍历可迭代对象的集合（如数组对象、字符串、map、set等拥有迭代器的集合），不能遍历普通对象（因为对象不可迭代），并且可以正确的响应break、continue和return语句
  */
}

// 请用js去除字符串空格？
{
  let strTest = '  sdfsaf  ';
  obj.trim(); // 方法一：使用ES6语法或是JQ的trim方法
  // 方法二：使用正则方式/\s*/
  // 方法三：把字符串以空格拆成数组，然后合并成字符串obj.split(' ').join('')
}

// 你如何获取浏览器URL中查询字符串中的参数？
// 地址 http://www.runoob.com/jquery/misc-trim.html?channelid=12333&name=xiaoming&age=23
{
  /* 
  获取url整体链接，然后用正则匹配  以?号或&开头，=号到&或空结束，正则匹配回来的是数据，value值是在数据中的第1个位置(还没背下具体细代码)
  */
  function getQuery(name) {
    var reg = new RegExp('(^|&|\\?)' + name + '=([^&]*)(&|$)');
    var r = 'http://www.runoob.com/jquery/misc-trim.html?channelid=12333&name=xiaoming&age=23'
      .substr(1)
      .match(reg);
    // var r = window.location.search.substr(1).match(reg); // 从?之后开始匹配如getQuery(courseid)返回一个数组["courseid=8","","8","&",index:0,input:"courseid=8"]
    if (r != null) return unescape(r[2]);
    return null;
  }

  console.log(getQuery('channelid'));
}

// js 字符串操作函数你知道哪些？
{
  /* 
  s.indexOf() s.lastIndexOf()查找字符串第几个位置
  s.split() 转成数组
  s.search() 查找字符串第几位置
  s.replace() 把什么字符替换成什么
  s.toUpperCase()  .toLowerCase() 转换大小写
  s.slice() 读取几到几之间位置的字符串
  s.subString()  s.substr() 读取从第几位置到第几位置的字符串
  s.charAt() 读取字符串第几个位置，返回的是字符串的值
  */
}

// js中怎样添加、移除、移动、复制、创建和查找DOM节点？
{
  /* 能自己说出api加整合最佳
  创建createDocumentFragment() 创建一个dom片段
  createElement()创建元素
  cteateTextNode()创建文本节点

  2添加、移除、替换、插入
  appendChild()添加
  removeChild()移除
  replaceChild()替换
  insertBefore()插入

  3查找
  getElementsByTagName()查找标签
  getElementsByName() 查找元素的name属性的值
  getElementById() 查找元素的ID
  getElementsByClassName()查找class名为这个的元素

  TODO: 拼合成一个整体
  */
}

// typeof与instanceof二者的区别？
{
  /* 
  typeof检测类型，instanceof检测的是什么实例类型
  相同点：JavaScript 中 typeof 和 instanceof 常用来判断一个变量是否为空，或者是什么类型的。
  typeof的定义和用法：返回值是一个字符串，用来说明变量的数据类型。

  细节：
  (1)、typeof 一般只能返回如下几个结果：number,boolean,string,function,object,undefined。
  (2)、typeof 来获取一个变量是否存在，如 if(typeof a!="undefined"){alert("ok")}，而不要去使用 if(a) 因为如果 a 不存在（未声明）则会出错。
  (3)、对于 Array,Null 等特殊对象使用 typeof 一律返回 object，这正是 typeof 的局限性。

  Instanceof定义和用法：instanceof 用于判断一个变量是否属于某个对象的实例。
  */
}

// 以下代码块分别输出的是什么？为什么？
{
  {
    function test() {}
    test = 123;
  }
  console.log('第一个', test.toString());

  {
    function test() {
      console.log(2);
    }
    test = 123;
    function test() {
      console.log(22);
    }
  }
  console.log('第二个', test.toString());

  {
    function test() {
      console.log(3);
    }
    test = 123;
    function test() {
      console.log(33);
    }
    test = 234;
    console.log(test);
  }
  console.log('第三个', test.toString());
}

// 合并两个数组，说说你能有几种方法实现
{
  const _nums = [2, 7, 11, 15];
  const arr2 = [3, 5, 6, 7];

  // concat、for 循环逐个push插入、扩展运算法、push.apply 这些方法都可以
  _nums.push.apply(_nums, arr2);
  console.log(_nums);

  let newArr = [..._nums, ...arr2];
  console.log(newArr);
}

// 合并两个对象，说出可以用几种方式实现
{
  let obj1 = {
    a: 333,
  };
  let obj2 = {
    b: 'hello',
  };

  // Object.assign、扩展运算法、手写深浅拷贝、for in循环obj2对象逐个插入到obj1中去
  let newData = Object.assign(obj1, obj2);
  let newData2 = { ...obj1, ...obj2 };
  console.log(newData2);
}
